/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
* Copyright (C) 2012 David Callu
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version.  The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* OpenSceneGraph Public License for more details.
*/

#ifndef _BUFFEROBJECT_H
#define _BUFFEROBJECT_H 1

#include <kernel/Object.h>
#include <iosfwd>
#include <list>
#include <map>

namespace FD
{
	namespace Kernel
	{
		class JAS_Kernel_Export BufferData : public FD::Kernel::Object
		{
		public:

			BufferData():
			  Object(true),
				  _modifiedCount(0),
				  _bufferIndex(0),
				  _numClients(0) {}

			  /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
			  BufferData(const BufferData& bd,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
			  FD::Kernel::Object(bd,copyop),
				  _modifiedCount(0),
				  _bufferIndex(0),
				  _modifiedCallback(bd._modifiedCallback),
				  _numClients(0) {}

			  virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const BufferData*>(obj)!=NULL; }
			  virtual const char* libraryName() const { return "Fd.XKernel"; }
			  virtual const char* className() const { return "BufferData"; }

			  virtual const void*   getDataPointer() const = 0;
			  virtual unsigned int    getTotalDataSize() const = 0;

			  virtual osg::Array* asArray() { return 0; }
			  virtual const osg::Array* asArray() const { return 0; }

			  void setBufferObject(BufferObject* bufferObject);
			  BufferObject* getBufferObject() { return _bufferObject.get(); }
			  const BufferObject* getBufferObject() const { return _bufferObject.get(); }

			  void setBufferIndex(unsigned int index) { _bufferIndex = index; }
			  unsigned int getBufferIndex() const { return _bufferIndex; }

			  GLBufferObject* getGLBufferObject(unsigned int contextID) const { return _bufferObject.valid() ? _bufferObject->getGLBufferObject(contextID) : 0; }
			  GLBufferObject* getOrCreateGLBufferObject(unsigned int contextID) const { return _bufferObject.valid() ? _bufferObject->getOrCreateGLBufferObject(contextID) : 0; }

			  struct ModifiedCallback : public virtual FD::Kernel::Object
			  {
				  ModifiedCallback() {}

				  ModifiedCallback(const ModifiedCallback&,const CopyOp&) {}

				  META_Object(osg,ModifiedCallback);

				  virtual void modified(BufferData* /*bufferData*/) const {}
			  };

			  void setModifiedCallback(ModifiedCallback* md) { _modifiedCallback = md; }
			  ModifiedCallback* getModifiedCallback() { return _modifiedCallback.get(); }
			  const ModifiedCallback* getModifiedCallback() const { return _modifiedCallback.get(); }

			  /** Dirty the primitive, which increments the modified count, to force buffer objects to update.
			  * If a ModifiedCallback is attached to this BufferData then the callback is called prior to the bufferObject's dirty is called. */
			  inline void dirty()
			  {
				  ++_modifiedCount;
				  if (_modifiedCallback.valid()) _modifiedCallback->modified(this);
				  if (_bufferObject.valid()) _bufferObject->dirty();
			  }

			  /** Set the modified count value.*/
			  inline void setModifiedCount(unsigned int value) { _modifiedCount=value; }

			  /** Get modified count value.*/
			  inline unsigned int getModifiedCount() const { return _modifiedCount; }

			  /** Resize any per context GLObject buffers to specified size. */
			  virtual void resizeGLObjectBuffers(unsigned int maxSize);

			  /** If State is non-zero, this function releases OpenGL objects for
			  * the specified graphics context. Otherwise, releases OpenGL objects
			  * for all graphics contexts. */
			  void releaseGLObjects(State* state=0) const;

			  unsigned int getNumClients() const { return _numClients; }

			  void addClient(osg::Object * /*client*/) { ++_numClients; }

			  void removeClient(osg::Object * /*client*/) { --_numClients; }

		protected:

			virtual ~BufferData();

			unsigned int                    _modifiedCount;

			unsigned int                    _bufferIndex;
			osg::ref_ptr<BufferObject>      _bufferObject;
			osg::ref_ptr<ModifiedCallback>  _modifiedCallback;

			unsigned int _numClients;
		};
	}
}

#endif
